import dayjs, { Dayjs } from 'dayjs';
import os, { NetworkInterfaceInfo } from 'os';

// ------------------------------
// 1. 实体映射工具（原 getEntities，补全类型）
// ------------------------------
// 定义实体类型（根据项目实际实体扩展）
type EntityType = 'User' | 'Logs' | 'Roles' | 'Menus' | 'Auth' | undefined;

/**
 * 根据请求路径映射对应实体（如 /users → User）
 * @param path 请求路径（如 /users/list）
 * @returns 对应的实体标识（无匹配时返回 undefined）
 */
export const getEntities = (path: string): EntityType => {
    const pathToEntityMap: Record<string, EntityType> = {
        '/users': 'User',
        '/logs': 'Logs',
        '/roles': 'Roles',
        '/menus': 'Menus',
        '/auth': 'Auth',
    };

    // 遍历映射表，匹配首个符合前缀的路径
    for (const [prefix, entity] of Object.entries(pathToEntityMap)) {
        if (path.startsWith(prefix)) {
            return entity;
        }
    }
    return undefined;
};

// ------------------------------
// 2. 字节单位格式化（原 formatBytes，补全类型+容错）
// ------------------------------
type ByteUnit = 'Bytes' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB' | 'EB' | 'ZB' | 'YB';

/**
 * 将字节数格式化为易读单位（如 1024 → 1.00 KB）
 * @param bytes 字节数（需 ≥0）
 * @param decimals 保留小数位数（默认 2）
 * @returns 格式化后的字符串（如 "2.50 MB"）
 */
export function formatBytes(bytes: number, decimals = 2): string {
    // 容错：处理负数/NaN的情况
    if (isNaN(bytes) || bytes < 0) {
        console.warn('formatBytes: 字节数必须为非负数字，返回 "0 Bytes"');
        return '0 Bytes';
    }
    if (bytes === 0) return '0 Bytes';

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals; // 确保小数位数非负
    const units: ByteUnit[] = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const unitIndex = Math.floor(Math.log(bytes) / Math.log(k));

    // 计算并保留指定小数位
    const formattedValue = parseFloat((bytes / Math.pow(k, unitIndex)).toFixed(dm));
    return `${formattedValue} ${units[unitIndex]}`;
}

// ------------------------------
// 3. 随机数字生成（原 generateRandomNumber，补全类型+范围限制）
// ------------------------------
/**
 * 生成 N 位随机数字（首位非 0）
 * @param n 随机数长度（默认 15，范围 1~30，避免过长导致性能问题）
 * @returns N 位随机数字字符串
 */
export function generateRandomNumber(n = 15): string {
    // 容错：限制长度范围（1~30）
    const validLength = Math.max(1, Math.min(n, 30));
    if (validLength !== n) {
        console.warn(`generateRandomNumber: 长度需在 1~30 之间，已自动调整为 ${validLength} 位`);
    }

    const firstDigitPool = '123456789'; // 首位非 0
    const otherDigitPool = '0123456789'; // 后续位可包含 0

    // 生成首位数字
    const firstDigit = firstDigitPool[Math.floor(Math.random() * firstDigitPool.length)];
    // 生成剩余 (validLength - 1) 位数字
    const otherDigits = Array.from({ length: validLength - 1 }, () =>
      otherDigitPool[Math.floor(Math.random() * otherDigitPool.length)],
    ).join('');

    return firstDigit + otherDigits;
}

export const transformer = {
    to: (value: Date) => (value),
    from: (value: Date) => (`${dayjs(value).format("YYYY-MM-DD HH:mm:ss")}`)
};


export const customTransformer = {
    to: (value: Date) => (new Date(value)),
    from: (value: Date) => (value ? `${dayjs(value).format("YYYY-MM-DD HH:mm:ss")}` : value)
};

// ------------------------------
// 5. 请求参数格式化（原 argumentsToString，适配 Fastify + 类型）
// ------------------------------
import type { FastifyRequest } from 'fastify'; // 适配 Fastify 请求类型

/**
 * 将 Fastify 请求的参数（params/query/body）格式化为字符串
 * @param request Fastify 请求对象
 * @returns 格式化后的参数字符串（如 "params:{} query:{id:1} body:{name:'test'}"）
 */
export function argumentsToString(request: FastifyRequest): string {
    // 容错：处理 request 为 undefined 的情况
    if (!request) return 'request: undefined';

    const parts: string[] = [];
    // 拼接 params（若有值）
    if (request.params && Object.keys(request.params).length > 0) {
        parts.push(`params:${JSON.stringify(request.params)}`);
    }
    // 拼接 query（若有值）
    if (request.query && Object.keys(request.query).length > 0) {
        parts.push(`query:${JSON.stringify(request.query)}`);
    }
    // 拼接 body（若有值，注意：需确保 body 已被解析）
    if (request.body && Object.keys(request.body as Record<string, unknown>).length > 0) {
        parts.push(`body:${JSON.stringify(request.body)}`);
    }

    return parts.join(' ');
}

// ------------------------------
// 6. 菜单树形结构生成（原 generateMenuTree，补全类型）
// ------------------------------
// 定义菜单类型（根据项目实际字段扩展）
interface MenuItem {
    id: string | number;
    parentId: string | number | null;
    children?: MenuItem[];
    [key: string]: unknown; // 支持其他自定义字段
}

/**
 * 将扁平的菜单列表转为树形结构
 * @param data 扁平的菜单数组
 * @param parentId 根节点的父ID（默认 null，即顶级菜单）
 * @returns 树形结构的菜单数组
 */
export const generateMenuTree = (data: MenuItem[], parentId: string | number | null = null): MenuItem[] => {
    // 容错：处理空数组
    if (!Array.isArray(data) || data.length === 0) return [];

    return data.reduce((tree: MenuItem[], menu) => {
        // 匹配当前菜单的父ID
        if (menu.parentId === parentId) {
            // 递归查找子菜单
            const children = generateMenuTree(data, menu.id);
            if (children.length > 0) {
                menu.children = children;
            }
            tree.push(menu);
        }
        return tree;
    }, []);
};

// ------------------------------
// 7. 本机IP获取（原 getLocalIP，核心优化：类型+容错+多环境适配）
// ------------------------------
/**
 * 获取本机有效 IPv4 地址（适配 Fastify 生产环境，过滤虚拟网卡）
 * @param preferredInterface 可选：优先获取的网卡名称（如 Docker 的 eth0、服务器的 ens33）
 * @returns 有效 IPv4 地址（无有效地址时返回默认 127.0.0.1）
 */
export function getLocalIP(preferredInterface?: string): string {
    try {
        const networkInterfaces = os.networkInterfaces();
        if (!networkInterfaces || Object.keys(networkInterfaces).length === 0) {
            console.warn('getLocalIP: 未检测到网络接口，返回默认IP 127.0.0.1');
            return '127.0.0.1';
        }

        // 1. 优先处理指定的网卡（如 Docker 的 eth0）
        if (preferredInterface && networkInterfaces[preferredInterface]) {
            const validIp = networkInterfaces[preferredInterface].find((info: NetworkInterfaceInfo) =>
              isEffectiveIpv4(info),
            );
            if (validIp) return validIp.address;
        }

        // 2. 遍历所有网卡，关键：先检查 ifInfos 是否存在且为数组
        for (const [ifName, ifInfos] of Object.entries(networkInterfaces)) {
            // 新增：检查 ifInfos 是否存在（解决 TS18048），不存在则跳过当前网卡
            if (!ifInfos || !Array.isArray(ifInfos)) {
                console.warn(`getLocalIP: 网卡 ${ifName} 的信息为空或非数组，跳过`);
                continue;
            }

            // 过滤虚拟网卡（VMware/VirtualBox/回环接口等）
            const isVirtualInterface = ifName.includes('VMware')
              || ifName.includes('VirtualBox')
              || ifName.includes('Loopback')
              || ifName.startsWith('veth'); // Docker 虚拟网卡前缀
            if (isVirtualInterface) continue;

            // 现在 ifInfos 已确认是数组，可安全调用 find
            const validIp = ifInfos.find((info: NetworkInterfaceInfo) => isEffectiveIpv4(info));
            if (validIp) return validIp.address;
        }

        // 3. 无任何有效IP时返回默认值
        console.warn('getLocalIP: 未找到有效 IPv4 地址，返回默认IP 127.0.0.1');
        return '127.0.0.1';
    } catch (error) {
        console.error('getLocalIP: 获取IP失败', (error as Error).message);
        return '127.0.0.1';
    }
}

/**
 * 辅助函数：判断是否为有效 IPv4（非回环、非内部地址）
 */
function isEffectiveIpv4(info: NetworkInterfaceInfo): boolean {
    return (
      info.family === 'IPv4' // 仅保留 IPv4
      && info.address !== '127.0.0.1' // 排除本地回环地址
      && !info.internal // 排除内部地址（如 Docker 桥接、虚拟机内网）
    );
}

// ------------------------------
// 8. 嵌套对象扁平化（原 extractNestedProperties/extractNestedPropertiesFromArray，补全类型+容错）
// ------------------------------
/**
 * 将嵌套对象扁平化为单层对象（如 {a: {b: 1}} → {a_b: 1}）
 * @param obj 待扁平化的对象
 * @param parentKey 父级键名（内部递归用，外部调用无需传）
 * @returns 扁平化后的对象
 */
function extractNestedProperties(
  obj: Record<string, unknown>,
  parentKey = '',
): Record<string, unknown> {
    const result: Record<string, unknown> = {};

    // 容错：处理非对象类型
    if (typeof obj !== 'object' || obj === null) {
        return parentKey ? { [parentKey]: obj } : {};
    }

    // 遍历对象属性，递归处理嵌套
    for (const [key, value] of Object.entries(obj)) {
        const currentKey = parentKey ? `${parentKey}_${key}` : key; // 拼接父键+当前键

        if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
            // 递归处理嵌套对象
            Object.assign(result, extractNestedProperties(value as Record<string, unknown>, currentKey));
        } else {
            // 非对象类型直接赋值
            result[currentKey] = value;
        }
    }

    return result;
}

/**
 * 将数组中的嵌套对象批量扁平化为单层对象
 * @param arr 包含嵌套对象的数组
 * @returns 扁平化后的对象数组
 */
export function extractNestedPropertiesFromArray(
  arr: Record<string, unknown>[],
): Record<string, unknown>[] {
    // 容错：处理非数组/空数组
    if (!Array.isArray(arr)) {
        console.warn('extractNestedPropertiesFromArray: 输入必须为数组，返回空数组');
        return [];
    }
    return arr.map(item => extractNestedProperties(item));
}